// - DATA ASSUMPTIONS - 
//
// MEETINGS
// + all meetings have an id and a start data
// 
// MEMBERS
// + all members have a "short" name
// 
// GROUPS
// + all groups have a "short" name

var WTO = {};

WTO.ALL_MEMBERS = "WTO";
WTO.BASE_URL = "http://wto-informals.org";
// http://kids.yahoo.com/reference/world-factbook/country/xx--World
// TODO(rae) P2 This should actually be from the same source as our population
// data
// TODO(rae) P3 we should update this automagically
WTO.WORLD_POPULATION =  6602224175;
WTO.MONTHS = [ "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
              "Sep", "Oct", "Nov", "Dec" ];

function glog(msg) {
  if (WTO && WTO.view && WTO.view.debug) {
    google.maps.Log.write(msg);
  }
}

/* Each callback just saves the relevant data. */
function member_callback(members) { WTO.members = members; }
function meeting_callback(meetings) { WTO.meetings = meetings; }
function group_callback(groups) { WTO.groups = groups; }


/* Represents the perspective we've chosen.
  TODO(rae): encode this in the URL so people can pass filters etc around
*/
WTO.view = {
  /* The currently selected display order for groups (and, implicitly the (TODO)
   * parenthetical information displayed beside the group.) */
  //"groupOrder": "members",
  /* The "tags" entry is actually a list of indices into the WTO.tagList array
   * of tags.  It's parsed at load time and put in the URL in a friendly
   * format. */
  "tags": [],
  "debug":true
};

WTO.drawGroups = function(order) {
  glog("drawing groups: " + order);

  GROUP_ORDERS = {
    "members": function(group) { 
      return $$.keys(group["members"]).length;
    }, 
    "gdp": function(group) { 
      var pop = 0;
      for (var iso in group["members"]) {
        gdp += (WTO.members[iso].gdp || 0);
      }
      return gdp;
    },
    "pop": function(group) { 
      var pop = 0;
      for (var iso in group["members"]) {
        if (!WTO.members[iso]) {
          glog("iso " + iso + " unknown");
          continue;
        }
        pop += (WTO.members[iso].pop || 0);
      }
      return pop;
    },
    "trade": function(group) { 
      var trade = 0;
      for (var iso in group["members"]) {
        trade += (WTO.members[iso].trade || 0);
      }
      return trade;
    },
    "name": function(group) {
      return group["short"];
    }
    // TODO(rae) P2 implement other ordering functions
    // per-cap gdp, trade (need data)
  };

  function percent(denominator) {
    return function(pop, name) {
      pop = (pop / denominator) * 100;
      if (pop < 1 && pop > 0) {
        return "[<1%] " + name;
      } else {
        return "[" + Math.round(pop) + "%] " + name;
      }
    }
  }

  function integer(val, name) {
    return "[" + Math.round(val) + "] " + name;
  }

  GROUP_FORMATS = {
    "pop": percent(WTO.WORLD_POPULATION),
    "name": function(val, name) {
      return name;
    },
    "members": integer,
    "trade": percent(WTO.WORLD_TRADE), // TODO(rae) define this const
    "gdp_per_capita": integer
  };

  // Clear the previous selection, if any, and set the new one
  if (WTO.view.groupOrder) {
    var orderE = $$.get("groups_by_" + WTO.view.groupOrder);
    if (orderE) { orderE.className = "jslink"; }
  }
  var orderE = $$.get("groups_by_" + order);
  if (orderE) { 
    orderE.className = "jslink current"; 
    WTO.view.groupOrder = order;
  }

  // Cache whatever values we'll use to sort the list
  var sortCache = {};
  for (var group in WTO.groups) { 
    //TODO(rae) P1 make the assumption about "short" existing more robust
    sortCache[group] = GROUP_ORDERS[order](WTO.groups[group]);
  }

  function cacheComparator(left, right) {
    left = sortCache[left];
    right = sortCache[right];
    // TODO(rae) P3 this special-casing of name is lame.
    if (order == "name") {
      if (left < right) { return -1; } 
      else if (left > right) { return 1; }
      else { return 0; }
    } else {
      // descending sort
      return right - left;
    }
  }

  var groups = $$.keys(WTO.groups);
  groups.sort(cacheComparator);

  var groupSelectE = $$.get("groupList");
  groupSelectE.innerHTML = "";

  // Map from tagname to true for speed.
  // TODO(rae) P2 remove fake seed-tags when we have real tags
  var tags = { "fake": true, "not real": true, "imaginary": true };

  for (var i = 0; i < groups.length; i++) {
    var short_ = groups[i];
    var group = WTO.groups[short_];
    var official = group["official"];

    var optionE = $$.make("option");
    optionE.value = short_;
    optionE.name = official;

    var name = short_;
    if (official && official.length < 32) {
      name = official;
    }

    if (GROUP_FORMATS[order]) {
      optionE.innerHTML = GROUP_FORMATS[order](sortCache[short_], name);
    } else {
      optionE.innerHTML = name;
    }

    groupSelectE.appendChild(optionE);
    if (group["tags"]) { 
      for (var i = 0; i < group["tags"].length; i++) {
        tags[group["tags"][i]] = true;
      }
    }
  }

  WTO.tagList = $$.keys(tags).sort();

};

WTO.drawTagList = function() { 
  var tagListE = $$.get("groupTags");

  // see it with a clickable null-option.
  tagListE.innerHTML = "<option value='-'>-</option>";

  for(var i = 0; i < WTO.tagList.length; i++) {
    var tag  = WTO.tagList[i];
    var optionE = $$.make("option");
    optionE.innerHTML = tag;
    optionE.value = ("tag_" + i);
    optionE.id = ("tag_" + i);
    tagListE.appendChild(optionE);
  }
};

WTO.drawCurrentTags = function() { 
  var listE = $$.get("tagList");
  
  listE.innerHTML = "";
  glog("drawing tags");

  for (var i = 0; i < WTO.view.tags.length; i++) {
    if (i > 0) { listE.innerHTML += "; "; }
    var tagNum = WTO.view.tags[i];
    var tagE = $$.make("span");
    tagE.className = "jslink";
    tagE.id = "active_tag_" + tagNum;
    tagE.innerHTML = WTO.tagList[tagNum];
    listE.appendChild(tagE);
  }
};

WTO.addTag = function() {
  var tagList = $$.get("groupTags");
  glog("Handling tag click");

  var idx = tagList.selectedIndex;
  // the first option is the "null" option
  if (idx >= 1) {
    var opt = tagList.options[idx];
    if (opt.disabled) { return; } // shouldn't happen
    opt.disabled = true;
    tagList.selectedIndex = 0;
    // ASSERT: opt.value.substring(0, 4) == "tag_"
    WTO.view.tags.push(Number(opt.value.substring(4)));
    WTO.drawCurrentTags();
  }
};

WTO.handleTagRemove = function(e) {
  var TAG_REMOVE_PREFIX = "active_tag_";
  glog("tagremove");
  if (e && e.target && e.target.id.startsWith(TAG_REMOVE_PREFIX)) {
    var tagNum = Number(e.target.id.substring(TAG_REMOVE_PREFIX.length));
    glog("Handling tag remove: " + tagNum);
    for (var i = 0; i < WTO.view.tags.length; i++) {
      if (WTO.view.tags[i] == tagNum) {
        WTO.view.tags.splice(i, 1);
        $$.get("tag_" + tagNum).disabled = false;
        break;
      }
    }
    WTO.drawCurrentTags();
  }
};

WTO.handleGroupOrder = function(e) { 
  var GROUPS_BY_PREFIX = "groups_by_";

  glog("grouporder");
  if (e && e.target && e.target.id.startsWith(GROUPS_BY_PREFIX)) {
    var order = e.target.id.substring(GROUPS_BY_PREFIX.length);
    glog("Group reordering: " + order);
    WTO.drawGroups(order);
  }
};

WTO.handleGroupChange = function(e) {
  glog("groupchange");
  if (e && e.target && e.target.id == "groupList") {
    WTO.populateGroup(e.target.value);
    return true;
  }
};

WTO.handleMeetingClick = function(e) {
  // FIXME need to handle:
  // year click
  // short -> long -> short
  // eventually, tag + member clicks

  glog("meetingclick");
  if (e && e.target) {
    if (e.target.className == "year") {
      var table = e.target.parentNode.parentNode;
      if (table.className == "collapsed") {
        table.className = "";
      } else {
        table.className = "collapsed";
      }
    } else {
      // parent is the tr; lastchild of parent is the td; lastchild of that is
      // long.  nextSibling of firstChild of td is the "short" version.
      var trE = e.target.parentNode;
      while (trE.tagName && trE.tagName != "TR") { trE = trE.parentNode; }
      var shortE = trE.lastChild.firstChild.nextSibling;
      var longE = trE.lastChild.lastChild;

      if (shortE.style.display == "none") {
        shortE.style.display = "block";
        longE.style.display = "none";
      } else {
        shortE.style.display = "none";
        longE.style.display = "block";
      }
    }
  }

}

WTO.renderMeetings = function(e) {

  function niceDate(meeting) {
    var startMonth = Number(meeting["start"].substring(5, 7));
    var startDay = Number(meeting["start"].substring(9)) || "";

    var result = WTO.MONTHS[startMonth] + " " + startDay;
    if (meeting["end"]) {
      result += " to ";
      var endMonth = Number(meeting["end"].substring(5, 7));
      var endDay = Number(meeting["end"].substring(9)) || "";
      if (endMonth != startMonth) {
        result += WTO.MONTHS[endMonth];
      }
      // TODO(jfh) remove end dates where they're the same as the start dates
      // TODO(rae) this is an invalid assumption and should be removed once
      // jesse does his bit (consider Sep 30 - Oct 30)
      if (endDay != startDay) {
        result += " " + endDay;
      }
    }
    return result;
        
  }

  /** Render a single meeting with a title, a long and a short form, and attach
   * it to a parent. */
  function renderMeeting(meeting, tableE) {
    var trE = $$.make("tr");
    trE.id = "meeting" + meeting["id"];
    trE.className = "meeting";

    var thE = $$.make("th");
    var content = "";
    content += niceDate(meeting);
    if (meeting["location"]) {
      content += "<br>" + meeting["location"].reverse().join(", ");
      // reverse is inplace, so we have to undo it!
      meeting["location"].reverse();
    }
    thE.innerHTML = content;
    trE.appendChild(thE);

    var tdE = $$.make("td");

    var divE;

    // draw title
    divE = $$.make("div");
    divE.id = 
    divE.className = "title";
    divE.innerHTML = meeting["name"];
    tdE.appendChild(divE);

    // draw short version -- just the iso codes of participants
    divE = $$.make("div");
    divE.id = "meeting" + meeting["id"] + "_short";
    divE.className = "short";
    if (meeting["attendees"]) { 
      divE.innerHTML = $$.keys(meeting["attendees"]).join(", ");
    }
    tdE.appendChild(divE);

    // draw long version -- full participant names, tags, URLs, metadata
    divE = $$.make("div");
    divE.id = "meeting" + meeting["id"] + "_long";
    divE.className = "long";
    divE.style.display = "none";

    var content = "";
    if (meeting["attendees"]) { 
      content += "Members: ";
      var isos = $$.keys(meeting["attendees"]);
      for (var i = 0; i < isos.length; i++) {
        if (i > 0) { content += ", "; }
        // TODO(rae) P2 make these clickable to add as a filter
        // TODO(jfh) P1 All member lists should be valid ISO lists
        if (WTO.members[isos[i]]) {
          content += WTO.members[isos[i]]["short"];
        } else {
          content += isos[i];
        }
      }
      content += "<br>";
    }

    if (meeting["tags"]) { 
      content += "Meeting tags: ";
      // TODO(rae) P2 make these clickable to add as a filter
      content += meeting["tags"].join(", ") + "<br>";
    }

    if (meeting["urls"]) { 
      content += "Documents: ";
      for (var name in meeting["urls"]) {
        content += "<a href='" + meeting["urls"][name] + "'>" + name + "</a> ";
      }
      content += "<br>";
    }

    if (meeting["notes"]) {
      content += "Note: " + meeting["notes"] + "<br>";
    }

    if (meeting["updated"]) {
      content += "Updated: " + meeting["updated"];
    }
    content += "(id: " + meeting["id"] + ")<br>";
    divE.innerHTML = content;

    tdE.appendChild(divE);
    trE.appendChild(tdE);
    tableE.appendChild(trE);
  }
  
  function meetingDateComparator(left, right) {
    if (left && right && left.start && right.start) {
      if (left.start < right.start) { return 1; }
      else if (left.start == right.start) { return 0; }
      else { return -1; }
    } else if (left && left.start) {
      // no right!
      return -1;
    } else {
      // no left!
      return 1;
    }
  };

  var meetings = $$.values(WTO.meetings).sort(meetingDateComparator);

  var year = "";
  var tableE;
  var counters = {};
  var meetingsE = $$.get("meetings");
  $$.clear(meetingsE);

  var start = new Date();

  for (var i = 0; i < meetings.length; i++) {
    var meeting = meetings[i];
    try {
      var meetingYear = meeting["start"].substring(0, 4);

      if (!counters[meetingYear]) { counters[meetingYear] = 0; }
      counters[meetingYear]++;
      
      if (meetingYear != year) {
        year = meetingYear;
        var tableE = $$.make("table");
        tableE.id = "meetings_" + year;

        tableE.className = "collapsed"; 

        var trE = $$.make("tr");
        var thE = $$.make("th");
        thE.id = "meetings_header_" + year;
        thE.className = "year";
        thE.colSpan = 2;
        thE.innerHTML = year;

        trE.appendChild(thE);
        tableE.appendChild(trE);
        meetingsE.appendChild(tableE);
      }
      renderMeeting(meeting, tableE);
    } catch (e) {
      glog("Failed meeting #" + i + " with: " + e);
    }
  }

  for (var year in counters) {
    if ($$.get("meetings_header_" + year)) {
      // Append the count to the old year
      $$.get("meetings_header_" + year).innerHTML += 
        " (" + counters[year] + " meetings)";
    }
  }

  var end = new Date();
  var time = (end.getTime() - start.getTime()) / 1000;
  glog("Meeting rendering took " + time);
  // 2008-01-05: ~0.75 seconds baseline
  // 2008-01-05: ~0.65 seconds with all years collapsed
  // 2008-01-05: ~0.205 seconds with all but 2007 collapsed and no glogging
  // 2008-01-05: ~0.225 seconds with none collapsed and no glogging
  // 2008-02-02: ~0.215 seconds with all collapsed and no glogging

};

WTO.populateGroup = function(group) {
  glog("populating " + group);
  var headers = [group, "Pop. (mn)", 
    "GDP/cap", "HDI", "joined"];
  var cols = [
    {"width": "50%", "className": "name"},
    {"width": "3*", "className": "number"},
    {"width": "5*", "className": "number"},
    {"width": "3*", "className": "number"},
    {"width": "8*", "className": "year"}
    ];

  WTO.view.group = group;
  
  var data = [];
  var rowIds = [];
  var group = WTO.groups[group];

  var markerOptions = {};
  markerOptions.clickable = true;
  markerOptions.draggable = false;
  var newBounds = new google.maps.LatLngBounds();
  WTO.map.clearOverlays();

  for (var iso in group["members"]) {
    var member = WTO.members[iso];
    // TODO(jfh) P1 purge bad ISO codes
    if (!member) { 
      glog("unknown ISO code '" + iso + "' in group " + group); 
      continue;
    }

    var name = member["official"] || member["short"];
    rowIds.push(iso);
    var joined = group["members"][iso];
    if (joined.length == 0) { 
      joined = "?" 
    } else if (joined.length % 2 == 0) {
      joined = "quit";
    } else {
      joined = joined[joined.length - 1];
    }
    data.push([
      name,
      Math.round(member["pop"] / 100000) / 10 || "?",
      Math.round(member["gdp"] / 10000000) / 100 || "?", 
      member["hdi"] || "?",
      joined]);

    if (member["lat"] && member["lng"]) {
      var latlng = new google.maps.LatLng(member["lat"], member["lng"]);
      markerOptions.title = name;
      var marker = new google.maps.Marker(latlng, markerOptions); 
      marker.iso = iso;
      WTO.map.addOverlay(marker);
      newBounds.extend(latlng);
    } else {
      glog(iso + " has no latlng data.");
    }
  }

  buildTable($$.get("members"), "member", headers, cols, data, rowIds);

  WTO.map.setCenter(newBounds.getCenter(), 
    Math.max(2, WTO.map.getBoundsZoomLevel(newBounds) - 1));


  /*
  var info = "<p>" + (group["info"] || "no info");
  if (group["links"]) {
    for (var name in group["links"]) {
      info += " | <a href='" + group["links"][name] + "'>" + name + "</a>";
    }
  }
  info += "</p>";
  $$.get("groupInfo").innerHTML = info;
  */

};

function buildTable(ancestor, idPrefix, headers, cols, data, rowIds) {

  function addCols(table) {
    for (var i = 0; i < cols.length; i++) {
      var col = $$.make("col");
      if (cols[i]["width"]) {
        col["width"] = cols[i]["width"];
      }
      table.appendChild(col);
    }
  }

  var table, headDivE, bodyDivE;

  $$.clear(ancestor);
  
  headDivE = $$.make("div");
  headDivE.id = idPrefix + "Thead";
  table = $$.make("table");
  addCols(table);

  var tHead = $$.make("thead");
  var hRow = $$.make("tr");
  for (var i = 0; i < headers.length; i++) {
    var th = $$.make("th");
    th.id = headDivE.id + i;
    th.innerHTML = headers[i];
    if (cols[i]["className"]) { th.className = cols[i]["className"]; }
    hRow.appendChild(th);
  }
  if (th) th.className = "lastColumn";
  tHead.appendChild(hRow)
  table.appendChild(tHead);
  headDivE.appendChild(table);

  bodyDivE = $$.make("div");
  bodyDivE.id = idPrefix + "Tdiv";
  table = $$.make("table");
  table.id = idPrefix + "Table";
  addCols(table);

  var tBody = $$.make("tbody");
  tBody.id = idPrefix + "Tbody";
  for (var i = 0; i < data.length; i++) {
    var dRow = $$.make("tr");
    if (data[i] && data[i].length) {
      if (rowIds) { dRow.id = rowIds[i]; }
      for (var j = 0; j < data[i].length; j++) {
        var td = $$.make("td");
        td.innerHTML = data[i][j];
        if (cols[j] && cols[j]["className"]) { 
          td.className = cols[j]["className"]; 
        }
        dRow.appendChild(td);
      }
      if (td) td.className = "lastColumn";
      tBody.appendChild(dRow);
    }
  }

  table.appendChild(tBody);
  bodyDivE.appendChild(table);

  // Swap the old out for the new
  $$.clear(ancestor);
  ancestor.appendChild(headDivE);
  ancestor.appendChild(bodyDivE);

  return table;
}

WTO.changeOverlay = function(url, callback) {
  WTO.overlay = new google.maps.GeoXml(url,
    function() {
      if (WTO.overlay.loadedCorrectly()) {
        WTO.map.clearOverlays();
        WTO.map.addOverlay(WTO.overlay);
        WTO.overlay.gotoDefaultViewport(WTO.map);
        if(callback) {
          try { callback(WTO.overlay); } catch(e) { 
            glog(e);
          }
        }
      } else {
        glog("Loading '" + url + "' failed.");
      }
      return true;
    }
  );
};

WTO.handleGroupClick = function(e) {
  var tr = e.target;
  while (tr.tagName && tr.tagName != "TR") { tr = tr.parentNode; }
  
  if (tr && tr.tagName == "TR" && tr.id) {
    var url = WTO.BASE_URL + "/data/groups/maps/" + tr.id + ".kml";
    //WTO.changeOverlay(url);
    WTO.populateGroup(tr.id.replace(/_/g, " "));
    $$.get("members").innerHTML = "";
    WTO.fixTableWidths();
  }
};

WTO.highlightMember = function(iso) {
  var url = WTO.BASE_URL + "/data/members/maps/" + iso + ".kml";
  glog("Highlighting " + iso);

  WTO.changeOverlay(url, function(overlay) {
    var member = WTO.members[iso];

    var tabs = [];

    /* Grooups tab */
    var groups = "";
    var groupCount = 0;
    var membership = "(not in WTO)";
    for (var i = 0; i < member["memberships"].length; i++) {
      var group = WTO.groups[member["memberships"][i]];
      if (group["short"] == "WTO") {
        membership = "";
      } else {
        groups += "<li onclick='WTO.populateGroup(\"" + group["short"] + "\")'>"
          + (group["official"] || group["short"]) + "</li>";
        groupCount++;
      }
    }

    var name = "<b>" + (member["official"] || member["short"]) + "</b> ";
    var groups = name + membership + "<ul class='groupList'>" + groups + "</ul>";
    tabs.push(
      new google.maps.InfoWindowTab("Groups (" + groupCount + ")", groups));
    
    /* Info tab */
    var info = name + "<br>pop: " + 
      (Math.round(member["pop"] / 100000) / 10 || "?") + "mn.<br>GDP: " +
      (member["gdp"] || "?") + 
      "<br>HDI: " + (member["hdi"] || "?") + "<br>" + 
      "<div>Lorem ispum blah blah blah</div>";
    tabs.push(new google.maps.InfoWindowTab("Info", info));

    WTO.map.openInfoWindowTabsHtml(overlay.getDefaultCenter(), tabs);
    return true;
  });
}

WTO.handleMembersClick = function(e) {
  glog("membersclick");
  // Did they click on a header element, or something in the table?

  if (e && e.target && e.target.id && e.target.id.startsWith("memberThead")) {
    WTO.handleHeaderClick(e);
  } else if (e && e.target) {
    var tr = e.target;

    while (tr.tagName && tr.tagName != "TR") { tr = tr.parentNode; }
    if (tr && tr.tagName == "TR" && tr.id) {
      var member = WTO.members[tr.id];
      WTO.highlightMember(member["iso"]);
    }
  }
};

WTO.handleHeaderClick = function(e) {
  var id = e.target.id;

  var baseId = id.replace(/Thead.*$/, "");
  var index = Number(id.replace(/^.*Thead/, ""));

  function rowNumberComparator(a, b) {
    a = Number(a.childNodes[index].innerHTML);
    b = Number(b.childNodes[index].innerHTML);
    if (isNaN(a)) { a = Number.NEGATIVE_INFINITY; }
    if (isNaN(b)) { b = Number.NEGATIVE_INFINITY; }
      
    return b - a; // descending sort
  }

  function rowStringComparator(a, b) {
    a = a.childNodes[index].innerHTML;
    b = b.childNodes[index].innerHTML;
    if (a > b) { return 1; } else if (a < b) { return -1; } else { return 0; }
  }
  
  var tbody = $$.get(baseId + "Tbody");
  var rows = [];
  for (var i = 0; i < tbody.childNodes.length; i++) {
    rows[i] = tbody.childNodes[i];
  }

  if (e.target.className == "number") {
    rows = rows.sort(rowNumberComparator);
  } else { 
    rows = rows.sort(rowStringComparator);
  }

  while (tbody.childNodes.length > 0) { 
    tbody.removeChild(tbody.firstChild);
  }
  for (var i = 0; i < rows.length; i++) {
    tbody.appendChild(rows[i]);
  }
  
};

WTO.fixTableWidths = function() { 
  $$.get("memberThead").style.width = 
    $$.get("memberTdiv").firstChild.offsetWidth;
};

WTO.getCountry = function(latlng) {
  glog("Requesting country for " + latlng);
  var head = document.getElementsByTagName("head")[0];

  var script = $$.make("script");
  script.type = "text/javascript";
  script.src = "http://ws.geonames.org/countrycode?lat=" + latlng.lat() +
    "&lng=" + latlng.lng() + "&type=JSON&callback=WTO.handleCountryClick";
  WTO.lastClick = latlng;
  head.appendChild(script);
  head.removeChild(script);
};

WTO.handleCountryClick = function(json) {
  var iso = json["countryCode"];
  if (iso) {
    WTO.highlightMember(iso);
  } else {
    glog("No country there!");
  }
};

WTO.initMap = function() { 
  WTO.map = new google.maps.Map2($$.get("map"));
  WTO.map.setCenter(new google.maps.LatLng(37.4419, -122.1419), 1);

  google.maps.Event.addListener(WTO.map, "click", function(overlay, point) {
      if (point) {
        WTO.getCountry(point);
      } else if (overlay) {
        if (overlay.iso) {
          WTO.highlightMember(overlay.iso);
        }
      }
    });


  var bounds = new google.maps.LatLngBounds(
    new google.maps.LatLng(-60, -179),
    new google.maps.LatLng(75, 180));
  WTO.map.setZoom(Math.max(2, WTO.map.getBoundsZoomLevel(bounds)));
  WTO.map.setCenter(bounds.getCenter());

  WTO.geocoder = new google.maps.ClientGeocoder();

}

function init() {
  if (GBrowserIsCompatible()) {
    WTO.initMap();
  }
  WTO.loaded = true;

  WTO.drawGroups("members");
  WTO.drawTagList();

  $$.get("tagList").addEventListener("click", WTO.handleTagRemove, false);
  $$.get("groupOrder").addEventListener("click", WTO.handleGroupOrder, false);
  $$.get("groupList").addEventListener("change", WTO.handleGroupChange, false);
  $$.get("meetings").addEventListener("click", WTO.handleMeetingClick, false);

  // FIXME handle case where members aren't loaded yet (impossible?)
  WTO.populateGroup(WTO.ALL_MEMBERS);

  WTO.fixTableWidths();
  //$$.get("groupTdiv").addEventListener('click', WTO.handleGroupClick, false);
  $$.get("members").addEventListener('click', WTO.handleMembersClick, false);

  // Synthesize a click to sort by name 
  WTO.handleMembersClick({"target": $$.get("memberThead0") });

  WTO.renderMeetings();

}


